const DISPLAY_OP_DELETE_NODE = 5;
const DISPLAY_OP_MOVE_NODE = 6;
const DISPLAY_OP_RESIZE_NODE = 7;
+const DISPLAY_OP_RESTACK_SURFACES = 8;
+const DISPLAY_OP_DELETE_SURFACE = 9;
// GdkCrossingMode
const GDK_CROSSING_NORMAL = 0;
var textures = {};
var stackingOrder = [];
var outstandingCommands = new Array();
+var outstandingDisplayCommands = null;
var inputSocket = null;
var debugDecoding = false;
var fakeInput = null;
this.url = window.URL.createObjectURL(blob);
this.refcount = 1;
this.id = id;
+
+ var image = new Image();
+ image.src = this.url;
+ this.image = image;
textures[id] = this;
}
function cmdRaiseSurface(id)
{
var surface = surfaces[id];
-
- moveToHelper(surface);
+ if (surface)
+ moveToHelper(surface);
}
function cmdLowerSurface(id)
{
var surface = surfaces[id];
- moveToHelper(surface, 0);
+ if (surface)
+ moveToHelper(surface, 0);
}
function TransformNodes(node_data, div, display_commands) {
image.height = rect.height;
image.style["position"] = "absolute";
set_rect_style(image, rect);
- var texture = textures[texture_id];
+ var texture = textures[texture_id].ref();
image.src = texture.url;
- texture.ref();
// Unref blob url when loaded
image.onload = function() { texture.unref(); };
newNode = image;
div.style["height"] = cmd[3] + "px";
break;
+ case DISPLAY_OP_RESTACK_SURFACES:
+ restackSurfaces();
+ break;
+ case DISPLAY_OP_DELETE_SURFACE:
+ var id = cmd[1];
+ delete surfaces[id];
+ break;
+
default:
alert("Unknown display op " + command);
}
}
}
-var active = false;
-function handleCommands(cmd)
+function handleCommands(cmd, display_commands, new_textures, modified_trees)
{
- if (!active) {
- start();
- active = true;
- }
-
- var display_commands = new Array();
+ var res = true;
var need_restack = false;
- while (cmd.pos < cmd.length) {
+ while (res && cmd.pos < cmd.length) {
var id, x, y, w, h, q, surface;
+ var saved_pos = cmd.pos;
var command = cmd.get_char();
lastSerial = cmd.get_32();
switch (command) {
var div = surface.div;
display_commands.push([DISPLAY_OP_DELETE_NODE, div]);
- delete surfaces[id];
+ // We need to delay this until its really deleted because we can still get events to it
+ display_commands.push([DISPLAY_OP_DELETE_SURFACE, id]);
break;
case BROADWAY_OP_ROUNDTRIP:
case BROADWAY_OP_UPLOAD_TEXTURE:
id = cmd.get_32();
var data = cmd.get_data();
- var texure = new Texture (id, data); // Stores a ref in textures
+ var texture = new Texture (id, data); // Stores a ref in global textures array
+ new_textures.push(texture);
break;
case BROADWAY_OP_RELEASE_TEXTURE:
case BROADWAY_OP_SET_NODES:
id = cmd.get_16();
- var node_data = cmd.get_nodes ();
- surface = surfaces[id];
- var transform_nodes = new TransformNodes (node_data, surface.div, display_commands);
- transform_nodes.execute();
+ if (id in modified_trees) {
+ // Can't modify the same dom tree in the same loop, bail out and do the first one
+ cmd.pos = saved_pos;
+ res = false;
+ } else {
+ modified_trees[id] = true;
+
+ var node_data = cmd.get_nodes ();
+ surface = surfaces[id];
+ var transform_nodes = new TransformNodes (node_data, surface.div, display_commands);
+ transform_nodes.execute();
+ }
break;
case BROADWAY_OP_GRAB_POINTER:
}
if (need_restack)
- restackSurfaces();
+ display_commands.push([DISPLAY_OP_RESTACK_SURFACES]);
- handleDisplayCommands(display_commands);
+ return res;
+}
- return true;
+function handleOutstandingDisplayCommands()
+{
+ if (outstandingDisplayCommands) {
+ window.requestAnimationFrame(
+ function () {
+ handleDisplayCommands(outstandingDisplayCommands);
+ outstandingDisplayCommands = null;
+
+ if (outstandingCommands.length > 0)
+ setTimeout(handleOutstanding);
+ });
+ } else {
+ if (outstandingCommands.length > 0)
+ handleOutstanding ();
+ }
}
+/* Mode of operation.
+ * We run all outstandingCommands, until either we run out of things
+ * to process, or we update the dom nodes of the same surface twice.
+ * Then we wait for all textures to load, and then we request am
+ * animation frame and apply the display changes. Then we loop back and
+ * handle outstanding commands again.
+ *
+ * The reason for stopping if we update the same tree twice is that
+ * the delta operations generally assume that the previous dom tree
+ * is in pristine condition.
+ */
function handleOutstanding()
{
+ var display_commands = new Array();
+ var new_textures = new Array();
+ var modified_trees = {};
+
+ if (outstandingDisplayCommands != null)
+ return;
+
while (outstandingCommands.length > 0) {
var cmd = outstandingCommands.shift();
- if (!handleCommands(cmd)) {
+ if (!handleCommands(cmd, display_commands, new_textures, modified_trees)) {
outstandingCommands.unshift(cmd);
- return;
+ break;
+ }
+ }
+
+ if (display_commands.length > 0)
+ outstandingDisplayCommands = display_commands;
+
+ if (new_textures.length > 0) {
+ var n_textures = new_textures.length;
+ for (var i = 0; i < new_textures.length; i++) {
+ var t = new_textures[i];
+ t.image.onload = function() {
+ n_textures -= 1;
+ if (n_textures == 0) {
+ handleOutstandingDisplayCommands();
+ }
+ };
}
+ } else {
+ handleOutstandingDisplayCommands();
}
+
}
function BinCommands(message) {
return data;
};
+var active = false;
function handleMessage(message)
{
+ if (!active) {
+ start();
+ active = true;
+ }
+
var cmd = new BinCommands(message);
outstandingCommands.push(cmd);
if (outstandingCommands.length == 1) {